home *** CD-ROM | disk | FTP | other *** search
- /* Written 8/12/93 by Max Hailperin <max@nic.gac.edu>, Math/CS department,
- Gustavus Adolphus College. Public domain, no warranty. Please share
- any improvements with me.
-
- Note: this communications protocol is for use with Best Fortress UPSs. */
-
- #import "daemon.h"
- #import "UPSController.h"
- #import <sys/ioctl.h>
- #import <sys/fcntl.h>
- #import <dpsclient/dpsclient.h>
- #import <appkit/Application.h>
- #import <stdio.h>
- #import <stdlib.h>
- #import <libc.h>
- #import <time.h>
- #import <ctype.h>
- #import <syslog.h>
-
- #ifndef POLLING_PERIOD
- #define POLLING_PERIOD 60.0
- #endif
- #ifndef RETRY_PERIOD
- #define RETRY_PERIOD 5.0
- #endif
- #ifndef RETRIES
- #define RETRIES 3
- #endif
- #ifndef SHUTDOWN_MINUTES
- #define SHUTDOWN_MINUTES 5
- #endif
- #ifndef SHUTDOWN_ALARM_MASK
- #define SHUTDOWN_ALARM_MASK 0x292
- #endif
- #ifndef SHUTDOWN_DELAY
- #define SHUTDOWN_DELAY 60
- #endif
- #ifndef POWERDOWN_DELAY
- #define POWERDOWN_DELAY 120
- #endif
-
- #define BYTES_IN_F_STRING 40
- #define HEX_DIGITS_PER_BYTE 2
-
- #define ctrl(c) ((c)&0x1f)
-
- static struct {
- int systemMode, inverterStatus, alarmStatus, ACInputVolts, ACOutputVolts,
- ACOutputDeciAmperes, ACLoadVA, batteryDeciVolts, deciHertz, minutes,
- ambientTemp, ROMVersion, time;
- } status;
-
- static DPSTimedEntry retryTE;
- static int retries;
-
- static int checksumOK(const unsigned char *fString){
- unsigned char sum=0;
- int i;
- for(i = 0; i < BYTES_IN_F_STRING; i++)
- sum += fString[i];
- return sum == 0;
- }
-
- static int asDecimal(const unsigned char *bytes, int count){
- int value=0;
- while(count--){
- value *= 100;
- value += 10*(*bytes/16) + *bytes % 16;
- bytes++;
- }
- return value;
- }
-
- static void describeTrouble(){
- if(status.inverterStatus)
- syslog(FACILITY|LOG_EMERG,
- "Running on battery power; backup time remaining: %d minutes.",
- status.minutes);
- if(status.alarmStatus & 0x1)
- syslog(FACILITY|LOG_EMERG, "UPS memory error");
- if(status.alarmStatus & 0x2)
- syslog(FACILITY|LOG_EMERG, "Check battery");
- if(status.alarmStatus & 0x4)
- syslog(FACILITY|LOG_EMERG, "Check inverter");
- if(status.alarmStatus & 0x8)
- syslog(FACILITY|LOG_EMERG, "Low runtime remaining");
- if(status.alarmStatus & 0x10)
- syslog(FACILITY|LOG_EMERG, "Low battery");
- if(status.alarmStatus & 0x20)
- syslog(FACILITY|LOG_EMERG, "Circuit breaker shutdown");
- if(status.alarmStatus & 0x40)
- syslog(FACILITY|LOG_EMERG, "Overload");
- if(status.alarmStatus & 0x80)
- syslog(FACILITY|LOG_EMERG, "High temperature");
- if(status.alarmStatus & 0x100)
- syslog(FACILITY|LOG_EMERG, "Site wiring fault");
- if(status.alarmStatus & 0x200)
- syslog(FACILITY|LOG_EMERG, "High battery");
- }
-
- static void doShutdown(FILE *ups){
- syslog(FACILITY|LOG_EMERG,
- "Warning!!! System shutdown in %d seconds; please log off NOW!",
- SHUTDOWN_DELAY);
- #ifndef DEBUG
- sleep(SHUTDOWN_DELAY);
- fprintf(ups, "off %d\r", POWERDOWN_DELAY);
- system("/usr/etc/halt -p");
- #endif
- }
-
- static void handleTrouble(FILE *ups){
- /* should do the filtering of transients like checkups somehow? */
- describeTrouble();
- if((status.alarmStatus & SHUTDOWN_ALARM_MASK) ||
- (status.inverterStatus && status.minutes < SHUTDOWN_MINUTES))
- doShutdown(ups);
- }
-
- static void processFString(const unsigned char *fString, FILE *ups){
- if(!checksumOK(fString))
- syslog(FACILITY|LOG_CRIT, "Checksum error in UPS F string");
- else{
- if(retryTE){
- DPSRemoveTimedEntry(retryTE);
- retryTE = 0;
- }
- status.systemMode = fString[5];
- status.inverterStatus = fString[8];
- status.alarmStatus = fString[11]<<8 + fString[10];
- status.ACInputVolts = asDecimal(fString+12, 2);
- status.ACOutputVolts = asDecimal(fString+14, 2);
- status.ACOutputDeciAmperes = asDecimal(fString+18, 2);
- status.ACLoadVA = asDecimal(fString+20, 3);
- status.batteryDeciVolts = asDecimal(fString+25, 2);
- status.deciHertz = asDecimal(fString+27, 2)/10;
- status.minutes = asDecimal(fString+29, 2);
- status.ambientTemp = asDecimal(fString+31, 2);
- status.ROMVersion = asDecimal(fString+37, 2);
- status.time = time(0);
- if(status.inverterStatus || status.alarmStatus)
- handleTrouble(ups);
- }
- }
-
- static void reallyRequestFString(FILE *ups){
- putc('f', ups);
- putc('\r', ups);
- }
-
- static void retry(DPSTimedEntry retryTE, double now, void *ups){
- #ifdef DEBUG
- printf(">>>retrying\n");
- #endif
- if(retries++ == RETRIES)
- syslog(FACILITY|LOG_CRIT, "Not succesfully communicating with UPS");
- putc(ctrl('q'), (FILE*)ups);
- reallyRequestFString((FILE*)ups);
- }
-
- static void requestFString(FILE *ups){
- if(!retryTE){
- retries = 0;
- retryTE= DPSAddTimedEntry(RETRY_PERIOD, retry, ups, NX_RUNMODALTHRESHOLD);
- reallyRequestFString(ups);
- }
- }
-
- static void handleInput(int fd, void* vfp){
- static unsigned char bytes[BYTES_IN_F_STRING];
- static char hexDigits[HEX_DIGITS_PER_BYTE+1];
- static unsigned char *bytePtr=bytes;
- static char *hexPtr=hexDigits;
- int c;
- FILE *fp = (FILE*)vfp;
-
- while((c = getc(fp)) != EOF){
- #ifdef DEBUG
- putchar(c);
- #endif
- if(c == '\r'
- && bytePtr-bytes == BYTES_IN_F_STRING
- && hexPtr == hexDigits){
- processFString(bytes, fp);
- bytePtr = bytes;
- } else if(!isxdigit(c)){
- if(c == '[' || c == ']' || c == '{' || c == '}')
- requestFString(fp);
- bytePtr = bytes;
- hexPtr = hexDigits;
- } else{
- *hexPtr++ = c;
- if(hexPtr-hexDigits == HEX_DIGITS_PER_BYTE){
- if(bytePtr-bytes == BYTES_IN_F_STRING){
- syslog(FACILITY|LOG_CRIT, "Too many hex digits in a row from UPS");
- bytePtr = bytes;
- }
- *bytePtr++ = strtol(hexDigits, NULL, 16);
- hexPtr = hexDigits;
- }
- }
- }
- clearerr(fp);
- }
-
- static void poll(DPSTimedEntry pollTE, double now, void *ups){
- requestFString((FILE*)ups);
- }
-
- @implementation UPSController
-
- -initOn: (const char *)tty
- {
- struct sgttyb modes = {B1200, B1200, 0, 0, RAW};
- FILE *ups;
- if((ups = fopen(tty, "r+")) == NULL){
- syslog(FACILITY|LOG_CRIT, "Can't open alleged tty %s; exiting.", tty);
- exit(1);
- }
- setbuf(ups, NULL);
- ioctl(fileno(ups), TIOCSETP, &modes);
- fcntl(fileno(ups), F_SETFL, FNDELAY);
- DPSAddFD(fileno(ups), handleInput, ups, NX_MODALRESPTHRESHOLD);
- requestFString(ups);
- DPSAddTimedEntry(POLLING_PERIOD, poll, ups, NX_RUNMODALTHRESHOLD);
- return self;
- }
-
- -(int)systemMode:(int*)systemMode inverterStatus:(int*)inverterStatus
- alarmStatus:(int*)alarmStatus ACInputVolts:(int*)ACInputVolts
- ACOutputVolts:(int*)ACOutputVolts
- ACOutputDeciAmperes:(int*)ACOutputDeciAmperes ACLoadVA:(int*)ACLoadVA
- batteryDeciVolts:(int*)batteryDeciVolts deciHertz:(int*)deciHertz
- minutes:(int*)minutes ambientTemp:(int*)ambientTemp
- ROMVersion:(int*)ROMVersion time:(int*)time
- {
- *systemMode = status.systemMode;
- *inverterStatus = status.inverterStatus;
- *alarmStatus = status.alarmStatus;
- *ACInputVolts = status.ACInputVolts;
- *ACOutputVolts = status.ACOutputVolts;
- *ACOutputDeciAmperes = status.ACOutputDeciAmperes;
- *ACLoadVA = status.ACLoadVA;
- *batteryDeciVolts = status.batteryDeciVolts;
- *deciHertz = status.deciHertz;
- *minutes = status.minutes;
- *ambientTemp = status.ambientTemp;
- *ROMVersion = status.ROMVersion;
- *time = status.time;
- return 0;
- }
- @end
-